package com.example.demo.base;

import com.baomidou.mybatisplus.mapper.BaseMapper;
import com.baomidou.mybatisplus.plugins.Page;
import org.apache.commons.lang3.RandomUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public abstract class BaseServiceImpl<M extends BaseMapper<T>, T extends BaseEntity> extends BaseServiceSupport<M,T> implements BaseService<T> {
  protected Logger logger = LoggerFactory.getLogger(this.getClass());

  /**
   * 随机记录种子数
   */
  private final static Integer RAND_SEED_NUMBER = 20;

  @Override
  @Transactional
  public T insertEntity(UpdateBuilder<T> updateBuilder) {
    T entity = updateBuilder.getEntity();
    if (entity == null) {
      return null;
    }
    this.insert(entity);
    return this.selectById(entity.getId());
  }

  @Override
  @Transactional
  public T insertEntity0(T entity) {
    UpdateBuilder<T> updateBuilder=new UpdateBuilder<>(entity);
    return insertEntity(updateBuilder);
  }

  @Override
  @Transactional
  public boolean insertEntities(List<T> entities){
    return this.insertBatch(entities);
  }

  @Override
  @Transactional
  public boolean insertEntities(List<T> entities,Integer flushSize){
    return this.insertBatch(entities,flushSize);
  }

  @Override
  @Transactional
  public boolean updateEntity(UpdateBuilder<T> updateBuilder) {
    if(CollectionUtils.isEmpty(updateBuilder.getFilters())){
      throw new IllegalArgumentException("sql where parameter is null");
    }
    T updateEntity = updateBuilder.getEntity();
//        updateBuilder.setEntity(null);
    IEntityWrapper wrapper = this.getWrapper(updateBuilder);
    wrapper.setEntity(null);
    if (updateEntity == null) {
      throw new IllegalArgumentException("sql set is null");
    }
    return this.update(updateEntity, wrapper);
  }


  @Override
  public T selectEntity(QueryBuilder queryBuilder) {
    if(CollectionUtils.isEmpty(queryBuilder.getFilters())&&queryBuilder.getEntity()==null){
      throw new IllegalArgumentException("sql where is null");
    }
    IEntityWrapper wrapper = this.getWrapper(queryBuilder);
    Object obj = this.selectOne(wrapper);
    return obj != null ? (T) obj : null;
  }

  @Override
  public T selectEntity0(T entity) {
    QueryBuilder<T> queryBuilder = new QueryBuilder<T>(entity);
    Object obj = selectEntity(queryBuilder);
    return obj != null ? (T) obj : null;
  }

  @Override
  public List<T> selectEntities(QueryBuilder queryBuilder) {
    IEntityWrapper wrapper = this.getWrapper(queryBuilder);
    List<T> entities = this.selectList(wrapper);
    return entities;
  }

  @Override
  public List<T> selectEntities0(T entity) {
    QueryBuilder<T> queryBuilder = new QueryBuilder<T>(entity);
    return selectEntities(queryBuilder);
  }

  @Override
  public Pageable<T> selectEntitiesByPage(QueryBuilder queryBuilder,Integer pageNum,Integer pageSize) {
    IEntityWrapper wrapper = this.getWrapper(queryBuilder);
    Pageable pageable = new Pageable(pageNum,pageSize);
    Page<T> page = this.selectPage(pageable, wrapper);
    page.setTotal(this.count(queryBuilder));
    return pageable.convert(page);
  }

  @Override
  public Pageable<T> selectEntitiesByPage(QueryBuilder queryBuilder,Integer pageNum,Integer pageSize,String orderByField,boolean isAsc) {
    IEntityWrapper wrapper = this.getWrapper(queryBuilder);
    Pageable pageable = new Pageable(pageNum,pageSize);
    if(isAsc){
      wrapper.orderAsc(Collections.singleton(orderByField));
    }else {
      wrapper.orderDesc(Collections.singleton(orderByField));
    }
    Page<T> page = this.selectPage(pageable, wrapper);
    return pageable.convert(page);
  }

  @Override
  public Pageable<T> selectEntitiesByPage0(T entity, Integer pageNum, Integer pageSize) {
    return selectEntitiesByPage(new QueryBuilder<T>(entity),pageNum,pageSize);
  }

  @Override
  public Integer count(QueryBuilder queryBuilder) {
    IEntityWrapper wrapper = this.getWrapper(queryBuilder);
    return this.selectCount(wrapper);
  }

  @Override
  public Integer count0(T entity) {
    return count(new QueryBuilder(entity));
  }


  @Override
  public List<T> selectEntitiesByRandom(QueryBuilder queryBuilder, Integer size) {
    return selectEntitiesByRandom(queryBuilder,size,RAND_SEED_NUMBER);
  }

  @Override
  public List<T> selectEntitiesByRandom(QueryBuilder queryBuilder, Integer size,Integer seed) {
    Integer count = count(queryBuilder);
    Integer limitRange = count > seed ? count - seed : 0;
    Integer limitRandStart = RandomUtils.nextInt(0, limitRange + 1);
    queryBuilder.addFilter(Filter.ge("id",limitRandStart));
    queryBuilder.addFilter(Filter.limit(seed));
    IEntityWrapper wrapper = this.getWrapper(queryBuilder);
    List<T> seedList = this.selectList(wrapper);
    return getRandomListFromSeeds(seedList,size);
  }

  @Override
  public List<T> selectEntitiesByRandom0(T entity, Integer size){
    return selectEntitiesByRandom(new QueryBuilder(entity),size,RAND_SEED_NUMBER);
  }

  @Override
  public List<T> selectEntitiesByRandom0(T entity, Integer size,Integer seed){
    return selectEntitiesByRandom(new QueryBuilder(entity),size,seed);
  }

  private List<T> getRandomListFromSeeds(List<T> seedList, Integer size) {
    List<T> resultList = new ArrayList<>();
    if (CollectionUtils.isEmpty(seedList) && size <= 0) {
      return resultList;
    }
    Integer rangeNum = seedList.size();
    if (rangeNum <= size) {
      return seedList;
    }
    for (int i = 0; i < size; i++) {
      Integer index = RandomUtils.nextInt(0, rangeNum);
      if(index<=rangeNum){
        T entity=seedList.get(index);
        resultList.add(entity);
        seedList.remove(entity);
        rangeNum=seedList.size();
      }
    }
    return resultList;
  }


}
